/*
 * Lexer.cpp
 *
 *  Created on: 15.09.2012
 *      Author: steiner
 */

#include <deque>
#include "Lexer.h"

namespace IO {

template<typename T> Lexer<T>::Lexer(basic_istream<T> * in,
		map<vector<T>, int> *tokenTable) :
		in(in), tokenTable(tokenTable) {
	buffer = new deque<vector<T> >();
	readFromBuffer = false;
	currentToken = new pair<vector<T>, int>();
	currentToken->first = new vector<T>();
	currentToken->second = -1;
	nextToken = NULL;
}

template<typename T> Lexer<T>::~Lexer() {
	delete currentToken;
	delete nextToken;
	delete buffer;
}

template<typename T> bool Lexer<T>::isNewline(T c) {
	return c == '\n';
}

template<typename T> bool Lexer<T>::isWhitespace(T c) {
	return c == ' ' || c == '\t';
}

template<typename T> T Lexer<T>::readNextChar() {
	if (buffer->empty())
		readFromBuffer = false;
	if (readFromBuffer) {
		T nextChar = buffer->front();
		buffer->pop_front();
		return nextChar;
	}
	if (in->eof()) {
		if (!buffer->empty()) {
			readFromBuffer = true;
			return readNextChar();
		} else
			return -1;
	} else
		return in->get();
}

template<typename T> map<vector<T>, int> *Lexer<T>::getPossibleTokenMatches(
		map<vector<T>, int> *compareTable, int compareIndex, T c) {
	map<vector<T>, int> possibleMatches;
	int minDiffToCompareIndex = compareIndex;
	pair<vector<T>, int> possibleMatch = NULL;
	for (typename map<vector<T>, int>::iterator tokenAndId =
			compareTable->begin(); tokenAndId != compareTable->end();
			++tokenAndId) {
		if (tokenAndId == NULL)
			continue;
		if (tokenAndId->first.size() <= compareIndex) {
			possibleMatches.insert(tokenAndId);
			int diffToCompareIndex = compareIndex - tokenAndId->first.size();
			if (diffToCompareIndex < minDiffToCompareIndex) {
				minDiffToCompareIndex = diffToCompareIndex;
				possibleMatch = tokenAndId;
			}
		} else if (tokenAndId->first.size() > compareIndex
				&& tokenAndId->first[compareIndex] == c) {
			possibleMatch = NULL;
			possibleMatches.insert(tokenAndId);
		}
	}
	if (possibleMatch != NULL) {
		possibleMatches.clear();
		possibleMatches.insert(possibleMatch);
	}

	return &possibleMatches;
}

template<typename T> bool Lexer<T>::next() {
	if (nextToken != NULL) {
		currentToken = nextToken;
		nextToken = NULL;
		return true;
	}
	T nextChar = -1;
	int compareIndex = 0;
	map<vector<T>, int> *possibleMatches = new map<vector<T>, int>(tokenTable);
	vector<T> possibleIdentifier;
	while ((nextChar = readNextChar()) >= 0) {
		if (isWhitespace(nextChar) && !possibleIdentifier.empty()
				&& buffer.empty()) {
			currentToken->first = possibleIdentifier;
			currentToken->second = -1;
			buffer->push_back(nextChar);
			readFromBuffer = true;
			return true;
		}
		possibleMatches = getPossibleTokenMatches(possibleMatches, compareIndex,
				nextChar);
		if (!possibleMatches->empty()) {
			if (possibleMatches->size() == 1 && possibleMatches[0].size() <= (compareIndex + 1)) {
				if (possibleIdentifier.empty()) {
					currentToken = possibleMatches[0];
					return true;
				} else {
					currentToken->first = possibleIdentifier;
					currentToken->second = -1;
					nextToken = possibleMatches[0];
					return true;
				}
			} else {
				buffer->push_back(nextChar);
        compareIndex++;
			}
		} else {
			if (!buffer.empty()) {
				if (!isWhitespace(buffer->front()))
					possibleIdentifier += buffer->front();
				buffer->pop_front();
				readFromBuffer = true;
			} else if (!isWhitespace(nextChar))
				possibleIdentifier += nextChar;
		}
	}
   
   
   
   
  /*if (nextToken != NULL) {
		currentToken = nextToken;
		nextToken = NULL;
		return true;
	}
	T nextChar = readNextChar();
  if (nextChar < 0)
    return false;
  int compareIndex = 0;
	map<vector<T>, int> *possibleMatches = new map<vector<T>, int>(tokenTable);
	vector<T> possibleIdentifier;
  do {
    if (possibleMatches.empty()) {
      if (!buffer.empty()) {
				if (!isWhitespace(buffer->front()))
					possibleIdentifier += buffer->front();
				buffer->pop_front();
				readFromBuffer = true;
			} else if (!isWhitespace(nextChar))
				possibleIdentifier += nextChar;
    }
    if (isWhitespace(nextChar) && !possibleIdentifier.empty()
				&& buffer.empty()) {
			currentToken->first = possibleIdentifier;
			currentToken->second = -1;
			buffer->push_back(nextChar);
			readFromBuffer = true;
			return true;
		}
    possibleMatches = getPossibleTokenMatches(possibleMatches, compareIndex,
				nextChar);
		if (!possibleMatches->empty()) {
			if (possibleMatches->size() == 1 && possibleMatches[0].size() <= (compareIndex + 1)) {
				if (possibleIdentifier.empty()) {
					currentToken = possibleMatches[0];
					return true;
				} else {
					currentToken->first = possibleIdentifier;
					currentToken->second = -1;
					nextToken = possibleMatches[0];
					return true;
				}
			} else {
				buffer->push_back(nextChar);
        compareIndex++;
			}
		} else
      continue;
  } while((nextChar = readNextChar()) >= 0);*/

	return false;
}

} /* namespace IO */
